package no.sogl.importer;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import no.sogl.domain.Person;
import no.sogl.util.ExcelHelper;
import no.sogl.util.SoglUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.ss.usermodel.Row;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

public class PersonEntry {
    private static final Log log = LogFactory.getLog(PersonEntry.class);
    private String n3sportId;
    private String gender;
    private String firstName;
    private String lastName;
    private String fullName;
    private Date birthDate;
    private Integer birthYear;
    private String postNo;
    private String address;
    private String city;
    private List<String> emails = new ArrayList<String>();
    private List<String> mobiles = new ArrayList<String>();
    private List<String> phones = new ArrayList<String>();
    private String memberType;
    private String mainMemberId;
    private String comment;
    private Date memberSince;
    private String postAddress;
    private String fullAddress;
    private String parentNames;

    public static class Builder {
        private String dateFormat = "MM/dd/yy";
        private boolean datesAsStrings = false;
        private Integer n3sportIdCol;
        private Integer genderCol;
        private Integer firstNameCol;
        private Integer lastNameCol;
        private Integer fullNameCol;
        private Integer birthDateCol;
        private Integer birthYearCol;
        private Integer postNoCol;
        private Integer addressCol;
        private Integer fullAddressCol;
        private Integer cityCol;
        private List<Integer> emailCol = new ArrayList<Integer>();
        private Integer jobPhoneCol;
        private Integer mobileCol;
        private Integer phoneCol;
        private Integer memberTypeCol;
        private Integer mainMemberIdCol;
        private Integer commentCol;
        private Integer memberSinceCol;
        private Integer postAddressCol;
        private List<Integer> parentNameCols = new ArrayList<Integer>();

        public PersonEntry build(Row row) {
            if (row == null) return null;
            if (firstNameCol == null && lastNameCol == null && fullNameCol == null)
                throw new RuntimeException("Misconfigured builder!");
            PersonEntry pe = new PersonEntry();
            if (firstNameCol != null) pe.setFirstName(ExcelHelper.getCellString(row.getCell(firstNameCol)).trim());
            if (lastNameCol != null) pe.setLastName(ExcelHelper.getCellString(row.getCell(lastNameCol)).trim());
            if (fullNameCol != null) pe.setFullName(ExcelHelper.getCellString(row.getCell(fullNameCol)).trim());

            if (!StringUtils.hasText(pe.getFirstName()) && !StringUtils.hasText(pe.getLastName())
                    && !StringUtils.hasText(pe.getFullName())) return null;

            if (n3sportIdCol != null)
                pe.setN3sportId(nullIfEmpty(ExcelHelper.getCellString(row.getCell(n3sportIdCol))).trim());
            if (genderCol != null) pe.setGender(nullIfEmpty(ExcelHelper.getCellString(row.getCell(genderCol))).trim());
            if (birthYearCol != null) {
                try {
                    pe.setBirthYear(ExcelHelper.getCellInteger(row.getCell(birthYearCol)));
                } catch (Exception e) {
                    // NOP
                }
            }
            if (birthDateCol != null) {
                if (!datesAsStrings) {
                    try {
                        pe.setBirthDate(ExcelHelper.getCellDate(row.getCell(birthDateCol), dateFormat));
                    } catch (Exception e) {
                        try {
                            pe.setBirthDate(ExcelHelper.getCellDate(row.getCell(birthDateCol), "dd.MM.yy"));
                        } catch (Exception e2) {
                            try {
                                pe.setBirthDate(ExcelHelper.getCellDate(row.getCell(birthDateCol), "dd.MM.yyyy"));
                            } catch (Exception e3) {
                                try {
                                    pe.setBirthYear(Integer.parseInt(ExcelHelper.getCellString(row.getCell(birthDateCol))));
                                } catch (Exception e4) {
                                    log.warn("Unable to determine date format for value " + row.getCell(birthDateCol)
                                            + ". Tried all usual suspects.", e3);
                                }
                            }
                        }
                    }
                } else {
                    String date = nullIfEmpty(ExcelHelper.getCellString(row.getCell(birthDateCol)));
                    if (date != null) {
                        try {
                            DateTime dt = DateTimeFormat.forPattern(dateFormat).parseDateTime(date);
                            if (dt != null) pe.setBirthDate(dt.toDate());
                        } catch (Exception e) {
                            try {
                                pe.setBirthYear(Integer.parseInt(date));
                            } catch (Exception e4) {
                                log.warn("Unable to determine date format for value " + date);
                            }

                        }
                    }
                }
            }
            if (postNoCol != null) pe.setPostNo(nullIfEmpty(ExcelHelper.getCellString(row.getCell(postNoCol))));
            if (addressCol != null) pe.setAddress(nullIfEmpty(ExcelHelper.getCellString(row.getCell(addressCol))));
            if (fullAddressCol != null) pe.setFullAddress(nullIfEmpty(ExcelHelper.getCellString(row.getCell(fullAddressCol))));
            if (cityCol != null) pe.setCity(nullIfEmpty(ExcelHelper.getCellString(row.getCell(cityCol))));
            if (!CollectionUtils.isEmpty(emailCol)) {
                for (int i : emailCol)
                    pe.setEmail(nullIfEmpty(ExcelHelper.getCellString(row.getCell(i))));
            }
            if (!CollectionUtils.isEmpty(parentNameCols)) {
                for (int i : parentNameCols){
                    String names = ExcelHelper.getCellString(row.getCell(i));
                    if ( !StringUtils.hasText(names)) continue;
                    if ( !StringUtils.hasText(pe.getParentNames())) pe.setParentNames(ExcelHelper.getCellString(row.getCell(i)));
                    else pe.setParentNames(pe.getParentNames() + "," + ExcelHelper.getCellString(row.getCell(i)));
                }
            }
            if (jobPhoneCol != null) pe.setPhone(nullIfEmpty(ExcelHelper.getCellString(row.getCell(jobPhoneCol))));
            if (mobileCol != null) pe.setMobile(nullIfEmpty(ExcelHelper.getCellString(row.getCell(mobileCol))));
            if (phoneCol != null) pe.setPhone(nullIfEmpty(ExcelHelper.getCellString(row.getCell(phoneCol))));
            if (memberTypeCol != null) pe.setMemberType(nullIfEmpty(ExcelHelper.getCellString(row.getCell(memberTypeCol))));
            if (mainMemberIdCol != null)
                pe.setMainMemberId(nullIfEmpty(ExcelHelper.getCellString(row.getCell(mainMemberIdCol))));
            if (commentCol != null) pe.setComment(nullIfEmpty(ExcelHelper.getCellString(row.getCell(commentCol))));
            if (memberSinceCol != null) {
                if (!datesAsStrings) pe.setMemberSince(ExcelHelper.getCellDate(row.getCell(memberSinceCol), dateFormat));
                else {
                    String date = nullIfEmpty(ExcelHelper.getCellString(row.getCell(memberSinceCol)));
                    if (date != null) {
                        try {
                            DateTime dt = DateTimeFormat.forPattern(dateFormat).parseDateTime(date);
                            if (dt != null) pe.setMemberSince(dt.toDate());
                        } catch (Exception e) {
                            // NOP
                        }
                    }
                }

            }
            if (postAddressCol != null) pe.setPostAddress(nullIfEmpty(ExcelHelper.getCellString(row.getCell(postAddressCol))));

            if (!isValid(pe)) {
                System.out.println("Invalid:" + pe);
                return null;
            }
            return pe;
        }

        private boolean isValid(PersonEntry pe) {
            if (!StringUtils.hasText(pe.getFirstName()) || !StringUtils.hasText(pe.getLastName())) return false;
            if (postNoCol != null && StringUtils.hasText(pe.getPostNo()) && !pe.getPostNo().matches("\\d+")) return false;
            if ((postNoCol != null && !StringUtils.hasText(pe.getPostNo()))
                    && (addressCol != null && !(StringUtils.hasText(pe.getAddress())))) return false;
            return true;
        }

        public Builder withDateFormat(String dateFormat) {
            this.dateFormat = dateFormat;
            return this;
        }

        public Builder withDatesAsStrings(boolean datesAsStrings) {
            this.datesAsStrings = datesAsStrings;
            return this;
        }

        public Builder withN3sportIdCol(Integer n3sportIdCol) {
            this.n3sportIdCol = n3sportIdCol;
            return this;
        }

        public Builder withGenderCol(Integer genderCol) {
            this.genderCol = genderCol;
            return this;
        }

        public Builder withFirstNameCol(Integer firstNameCol) {
            this.firstNameCol = firstNameCol;
            return this;
        }

        public Builder withLastNameCol(Integer lastNameCol) {
            this.lastNameCol = lastNameCol;
            return this;
        }

        public Builder withFullNameCol(Integer fullNameCol) {
            this.fullNameCol = fullNameCol;
            return this;
        }

        public Builder withBirthDateCol(Integer birthDateCol) {
            this.birthDateCol = birthDateCol;
            return this;
        }
        public Builder withBirthYearCol(Integer birthYearCol) {
            this.birthYearCol = birthYearCol;
            return this;
        }

        public Builder withPostNoCol(Integer postNoCol) {
            this.postNoCol = postNoCol;
            return this;
        }

        public Builder withAddressCol(Integer addressCol) {
            this.addressCol = addressCol;
            return this;
        }

        public Builder withFullAddressCol(Integer addressCol) {
            this.fullAddressCol = addressCol;
            return this;
        }

        public Builder withCityCol(Integer cityCol) {
            this.cityCol = cityCol;
            return this;
        }

        public Builder withEmailCol(Integer emailCol) {
            this.emailCol.add(emailCol);
            return this;
        }
        public Builder withParentNamesCol(Integer col) {
            this.parentNameCols.add(col);
            return this;
        }

        public Builder withJobPhoneCol(Integer jobPhoneCol) {
            this.jobPhoneCol = jobPhoneCol;
            return this;
        }

        public Builder withMobileCol(Integer mobileCol) {
            this.mobileCol = mobileCol;
            return this;
        }

        public Builder withPhoneCol(Integer phoneCol) {
            this.phoneCol = phoneCol;
            return this;
        }

        public Builder withMemberTypeCol(Integer memberTypeCol) {
            this.memberTypeCol = memberTypeCol;
            return this;
        }

        public Builder withMainMemberIdCol(Integer mainMemberIdCol) {
            this.mainMemberIdCol = mainMemberIdCol;
            return this;
        }

        public Builder withCommentCol(Integer commentCol) {
            this.commentCol = commentCol;
            return this;
        }

        public Builder withMemberSinceCol(Integer memberSinceCol) {
            this.memberSinceCol = memberSinceCol;
            return this;
        }

        public Builder withPostAddressCol(Integer postAddressCol) {
            this.postAddressCol = postAddressCol;
            return this;
        }
    }

    public String getN3sportId() {
        return n3sportId;
    }

    public void setN3sportId(String n3sportId) {
        this.n3sportId = n3sportId;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String name) {
        fullName = name;
        String[] firstNameLastName = Person.splitFullName(name);
        lastName = firstNameLastName[1];
        firstName = firstNameLastName[0];
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        if (birthDate == null) return;
        if (new DateTime(birthDate).isBefore(new DateTime().withDate(1910, 1, 1))) this.birthDate = null;
        else this.birthDate = birthDate;
    }

    public String getPostNo() {
        return postNo;
    }

    public void setPostNo(String postNo) {
        this.postNo = postNo;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public List<String> getEmails() {
        return emails;
    }

    /**
     * FIXME: Support multiple emails and phone nos! switch from job/work/mobile to phones
     * 
     * @param email
     */
    public void setEmail(String email) {
        if (!StringUtils.hasText(email)) return;
        this.emails.addAll(SoglUtils.mailRecipientsAsList(email));
    }

    public List<String> getMobiles() {
        return mobiles;
    }

    public void setMobile(String phone) {
        addPhoneNos(phone);
    }

    private void addPhoneNos(String phone) {
        if (!StringUtils.hasText(phone)) return;
        List<String> phones = SoglUtils.phonesAsList(phone);
        for (String p : phones) {
            if (SoglUtils.isMobile(p)) {
                this.mobiles.add(p);
            } else this.phones.add(p);
        }
    }

    public List<String> getPhones() {
        return phones;
    }

    public void setPhone(String phone) {
        addPhoneNos(phone);
    }

    public String getMemberType() {
        return memberType;
    }

    public void setMemberType(String memberType) {
        this.memberType = memberType;
    }

    public String getMainMemberId() {
        return mainMemberId;
    }

    public void setMainMemberId(String mainMemberId) {
        this.mainMemberId = mainMemberId;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Date getMemberSince() {
        return memberSince;
    }

    public void setMemberSince(Date memberSince) {
        if (birthDate == null) return;
        if (new DateTime(memberSince).isBefore(new DateTime().withDate(1910, 1, 1))) this.memberSince = null;
        else this.memberSince = memberSince;
    }

    public String getPostAddress() {
        return postAddress;
    }

    public void setPostAddress(String postAddress) {
        this.postAddress = postAddress;
    }

    private static String nullIfEmpty(String val) {
        if (!StringUtils.hasText(val)) return null;
        return val.trim();
    }

    public String getFullAddress() {
        return fullAddress;
    }

    public void setFullAddress(String fullAddress) {
        if (!StringUtils.hasText(fullAddress)) return;
        this.fullAddress = fullAddress;
        Matcher matcher = Pattern.compile("(?mis)(.+), *?(\\d+) +(.+)").matcher(fullAddress);
        if (!matcher.find()) {
            setAddress(fullAddress);
            return;
        }
        setAddress(matcher.group(1));
        setPostNo(matcher.group(2));
        setCity(matcher.group(3));
    }

    public Integer getBirthYear() {
        return birthYear;
    }

    public void setBirthYear(Integer birthYear) {
        this.birthYear = birthYear;
    }

    public String getParentNames() {
        return parentNames;
    }

    public void setParentNames(String parentNames) {
        this.parentNames = parentNames;
    }

    @Override
    public String toString() {
        return "PersonEntry [n3sportId=" + n3sportId + ", gender=" + gender + ", firstName=" + firstName + ", lastName="
                + lastName + ", fullName=" + fullName + ", birthDate=" + birthDate + ", postNo=" + postNo + ", address="
                + address + ", city=" + city + ", emails=" + emails + ", mobiles=" + mobiles + ", phones=" + phones
                + ", memberType=" + memberType + ", mainMemberId=" + mainMemberId + ", comment=" + comment + ", memberSince="
                + memberSince + ", postAddress=" + postAddress + "]";
    }

}
